# -*- coding:latin-1 -*-

import sys
import os
import time

from PyQt4 import QtCore
from Code.Constantes import *

import Code.XMotorRespuesta as XMotorRespuesta

# ===========================================================================================================================
class XMotor(QtCore.QProcess):

    def __init__( self, nombre, exe, liOpcionesUCI=None, nMultiPV=None, siMaximizaMultiPV=False ) :

        # Lanzamos ya el motor
        QtCore.QProcess.__init__( self )

        direxe = os.path.abspath(os.path.dirname(exe))
        self.setWorkingDirectory ( direxe )

        if exe.lower().endswith(".exe") and sys.platform == "linux2" :
            self.start( "/usr/bin/wine", [exe], mode = QtCore.QIODevice.ReadWrite )
        else :
            # self.start( exe, [], mode = QtCore.QIODevice.Unbuffered | QtCore.QIODevice.ReadWrite )
            self.start( exe, [], mode = QtCore.QIODevice.ReadWrite )

        self.waitForStarted()
        self.nMultiPV = nMultiPV

        # Control de lectura
        self._buffer = ""

        self.siDebug = False
        self.nomDebug = nombre

        self.guiDispatch = None
        self.segsTransicion = 0.6
        self.ultPV = None

        # Configuramos
        self.nombre = nombre
        self.uci = self.orden_uci( )

        if liOpcionesUCI :
            for opcion, valor in liOpcionesUCI :
                if valor is None : # button en motores externos
                    self.orden_ok( "setoption name %s"%opcion )
                else :
                    self.orden_ok( "setoption name %s value %s"%(opcion, valor) )

        if nMultiPV :
            self.ponMultiPV( nMultiPV )

    # -----------------------------------------------------------------------------------------
    def ponGuiDispatch( self, guiDispatch, segsTransicion ) :
        self.guiDispatch = guiDispatch
        self.segsTransicion = segsTransicion

    # -----------------------------------------------------------------------------------------
    def ponMultiPV( self, nMultiPV ) :
        self.orden_ok( "setoption name %s value %s"%( "MultiPV", nMultiPV) )
    # -----------------------------------------------------------------------------------------
    def flush( self ) :
        self.readAllStandardOutput()
        self._buffer = ""

    # -----------------------------------------------------------------------------------------
    def buffer( self ) :
        self._lee()
        resp = self._buffer
        self._buffer = ""
        return resp

    # -----------------------------------------------------------------------------------------
    def apagar( self ) :
        try :
            self.write( "quit\n" )
            self.close()
            self.kill()
        except :
            pass

    # -----------------------------------------------------------------------------------------
    def _lee( self ) :
        x = str(self.readAllStandardOutput())
        if x :
            self._buffer += x
            if self.guiDispatch :
                self.ultPV = None
                if " pv " in x :
                    li = x.split(" pv ")
                    if li :
                        li = li[-1].split(" ")
                        if li :
                            self.ultPV = li[0]

            if self.siDebug :
                prlkS( x )
            return True
        return False
    # -----------------------------------------------------------------------------------------
    def escribe( self, linea ) :
        self.write( str(linea) + "\n" )
        if self.siDebug :
            prlk( self.nomDebug, "W", linea )

    # -----------------------------------------------------------------------------------------
    # def esperaRespuesta( self, segundos = None ) :
        # if segundos :
            # for x in range(segundos) :
                # if self.waitForReadyRead(1000) :
                    # break
                # if self.dispatch :
                    # self.dispatch()
        # else :
            # self.waitForReadyRead()
        # return str(self.readAllStandardOutput())

    # -----------------------------------------------------------------------------------------
    def espera( self, txt, msStop ) :
        iniTiempo = time.clock()
        siGracia = False
        pevents = iniTiempo
        while True :
            if self._lee() :
                iniTiempo = time.clock()
                if txt in self._buffer :
                    if self._buffer.endswith( "\n" ) :
                        return True

            if (time.clock()-pevents) > self.segsTransicion  :
                QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.ExcludeUserInputEvents)
                if self.guiDispatch :
                    if not self.guiDispatch(self.ultPV) :
                        return False
                pevents = time.clock()

            queda = msStop - int((time.clock()-iniTiempo)*1000)
            if queda <= 0 :
                if siGracia :
                    return False
                else :
                    queda += 1000
                    msStop += 1000
                    self.escribe( "stop" )
                    siGracia = True
            self.waitForReadyRead(100)

    # -----------------------------------------------------------------------------------------
    def orden_ok( self, orden ) :
        self.escribe( orden )
        self.escribe( "isready" )
        self.espera( "readyok", 1000 )
        return self.buffer()

    # -----------------------------------------------------------------------------------------
    def orden_uci( self ) :
        self.escribe( "uci" )
        self.espera( "uciok", 5000 )
        return self.buffer()

    # -----------------------------------------------------------------------------------------
    def orden_bestmove( self, orden, msMaxTiempo ) :
        self.flush()
        self.escribe( orden )
        resp = self.espera( "bestmove", msMaxTiempo )
        if not resp :
            self.orden_ok( "stop" )
            return None
        return self.buffer()

    # -----------------------------------------------------------------------------------------
    def orden_infinito( self, busca, msMaxTiempo ) :
        self.flush()
        self.escribe( "go infinite" )
        self.espera( busca, msMaxTiempo )
        return self.orden_ok( "stop" )

    # -----------------------------------------------------------------------------------------
    def posicionPartida( self, partida ) :
        posInicial = "startpos" if partida.siFenInicial()  else "fen %s"%partida.iniPosicion.fen()
        li = [ jg.movimiento().lower() for jg in partida.liJugadas ]
        moves = " moves %s"%(" ".join(li)) if li else ""
        self.orden_ok( "position %s%s"%(posInicial,moves) )

    # -----------------------------------------------------------------------------------------
    def mejorMovP( self, partida, maxTiempo, maxProfundidad, siReturnTxt = False ) :
        self.posicionPartida(partida)
        return self._mejorMov( maxTiempo, maxProfundidad, siReturnTxt )
    # -----------------------------------------------------------------------------------------
    def mejorMovF( self, fen, maxTiempo, maxProfundidad, siReturnTxt = False ) :
        self.orden_ok( "position fen %s"%fen )
        return self._mejorMov( maxTiempo, maxProfundidad, siReturnTxt )
    # -----------------------------------------------------------------------------------------
    def _mejorMov( self, maxTiempo, maxProfundidad, siReturnTxt ) :
        env = "go"
        if maxTiempo :
            env += " movetime %d"%maxTiempo
        if maxProfundidad :
            env += " depth %d"%maxProfundidad

        msTiempo = maxTiempo if maxTiempo else 10000

        resp = self.orden_bestmove( env, msTiempo )
        if not resp :
            return None

        mrm = XMotorRespuesta.MRespuestaMotor( self.nombre )
        mrm.dispatch( resp, maxTiempo, maxProfundidad )
        mrm.maxTiempo = maxTiempo
        mrm.maxProfundidad = maxProfundidad
        if siReturnTxt :
            return mrm, resp
        else :
            return mrm

    # -----------------------------------------------------------------------------------------
    def mejorMovInfinitoP( self, partida, maxProfundidad ) :
        self.posicionPartida(partida)
        return self._mejorMovInfinito( maxProfundidad )
    # -----------------------------------------------------------------------------------------
    def mejorMovInfinitoF( self, fen, maxProfundidad ) :
        self.orden_ok( "position fen %s"%fen )
        return self._mejorMovInfinito( maxProfundidad )
    # -----------------------------------------------------------------------------------------
    def _mejorMovInfinito( self, maxProfundidad ) :
        busca = " depth %d "%maxProfundidad

        tiempo = maxProfundidad*2000
        if maxProfundidad > 9 :
            tiempo += (maxProfundidad-9)*20000

        resp = self.orden_infinito( busca, tiempo )
        if not resp :
            return None

        mrm = XMotorRespuesta.MRespuestaMotor( self.nombre )
        mrm.dispatch( resp, None, maxProfundidad )
        mrm.maxTiempo = None
        mrm.maxProfundidad = maxProfundidad
        return mrm
    # -----------------------------------------------------------------------------------------

    # -----------------------------------------------------------------------------------------
    def mejorMovInfinitoTiempoP( self, partida, maxTiempo ) :
        self.posicionPartida(partida)
        return self._mejorMovInfinitoTiempo( maxTiempo )
    # -----------------------------------------------------------------------------------------
    def mejorMovInfinitoTiempoF( self, fen, maxTiempo ) :
        self.orden_ok( "position fen %s"%fen )
        return self._mejorMovInfinitoTiempo( maxTiempo )
    # -----------------------------------------------------------------------------------------
    def _mejorMovInfinitoTiempo( self, maxTiempo ) :
        busca = " @@ " # que no busque nada
        resp = self.orden_infinito( busca, maxTiempo )
        if not resp :
            return None

        mrm = XMotorRespuesta.MRespuestaMotor( self.nombre )
        mrm.dispatch( resp, maxTiempo, None )
        mrm.maxTiempo = maxTiempo
        mrm.maxProfundidad = None
        return mrm
    # -----------------------------------------------------------------------------------------

    # -----------------------------------------------------------------------------------------
    def mejorMovTiempoP( self, partida, tiempoBlancas, tiempoNegras, tiempoJugada ) :
        self.posicionPartida(partida)
        return self._mejorMovTiempo( partida.ultPosicion.fen(), tiempoBlancas, tiempoNegras, tiempoJugada )
    # -----------------------------------------------------------------------------------------
    def mejorMovTiempoF( self, fen, tiempoBlancas, tiempoNegras, tiempoJugada ) :
        self.orden_ok( "position fen %s"%fen )
        return self._mejorMovTiempo( fen, tiempoBlancas, tiempoNegras, tiempoJugada )
    # -----------------------------------------------------------------------------------------
    def _mejorMovTiempo( self, fen, tiempoBlancas, tiempoNegras, tiempoJugada ) :
        env = "go wtime %d btime %d"%( tiempoBlancas, tiempoNegras )
        if tiempoJugada :
            env += " winc %d"%tiempoJugada
        maxtiempo = tiempoBlancas if " w " in fen else tiempoNegras
        resp = self.orden_bestmove( env, maxtiempo )
        if not resp :
            return None

        mrm = XMotorRespuesta.MRespuestaMotor( self.nombre )
        mrm.dispatch( resp, None, None )
        return mrm
    # -----------------------------------------------------------------------------------------

